package com.n9.qq.android.mock;

import com.n9.qq.android.*;
import com.n9.qq.android.RegisterProxySvcPack.*;
import com.n9.qq.android.firendlist.GetSimpleOnlineFriendInfoReq;
import com.n9.qq.android.firendlist.GetTroopListReqV2;
import com.n9.qq.android.firendlist.stTroopNum;
import com.n9.qq.android.login.*;
import com.n9.qq.android.login.tlv.TlvData;
import com.n9.qq.android.pb.SSOLoginMerge;
import com.n9.qq.client.FromServiceMsg;
import com.n9.qq.util.*;
import com.qq.jce.wup.UniPacket;
import com.tencent.mobileqq.pb.ByteStringMicro;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteBuffer;
import java.util.*;

/**
 * 模拟aq
 * @author ice
 * @since 2019/2/20 10:41
 */
public class MockAq {
    private static final Logger log = LoggerFactory.getLogger(MockAq.class);

    /**
     * 超时时间
     */
    private static final int TIME_OUT = 10000;

    private AqClient client;
    private AqContext context;
    private MsgListener msgListener;
    private HeartbeatThread heartbeatThread;//心跳线程,登陆成功后启动

    public MockAq(long qqUin, String password, MsgListener msgListener, OfflineListener offlineListener) {
        this.msgListener = msgListener;
        ServerAddrUtils.Addr next = ServerAddrUtils.next();
        client = new AqClient(next.getHost(), next.getPort(), msgListener);
        this.context = this.client.getContext();

        context.setQqUin(qqUin);
        context.setPw(password);
        context.setPwMd5(DigestUtils.md5(password));

        byte[] tgtgtKey = Util.randomBytes(16);
        long serverCurTime = System.currentTimeMillis()/1000;
        ShareKey shareKey = ShareKeyFactory.generateBC(Const.SERVER_PUB_KEY);
        byte[] randomKey = Util.randomBytes(16);

        context.setShareKey(shareKey);
        context.setTgtgtKey(tgtgtKey);
        context.setRandomKey(randomKey);
        context.setServerCurTime(serverCurTime);
        context.setOfflineListener(offlineListener);
    }

    public void setOfflineListener(OfflineListener offlineListener) {
        this.context.setOfflineListener(offlineListener);
    }

    /**
     * 重新登陆
     * @param imgHandler
     * @return
     */
    public LoginResult reLogin(VfCodeHandler imgHandler) {
        logout();

        ServerAddrUtils.Addr next = ServerAddrUtils.next();
        client = new AqClient(next.getHost(), next.getPort(), msgListener);
        return login(imgHandler);
    }

    public long getQqUin() {
        return this.context.getQqUin();
    }

    public String getPassword() {
        return this.context.getPw();
    }

    public int getGroupCount() {
        List<stTroopNum> list = context.getVecTroopList();
        return Objects.isNull(list) ? 0 : list.size();
    }


    public Status getStatus() {
        return this.context.getStatus();
    }

    /**
     * 执行登陆
     * @param imgHandler    验证码处理器
     * @return              登陆结果
     */
    public LoginResult login(VfCodeHandler imgHandler) {
        long qqUin = this.context.getQqUin();

        ReqLoginBody reqLogin = new ReqLoginBody(qqUin, context.getPwMd5(), context.getTgtgtKey(), context.getRequestId(), context.getServerCurTime(), DeviceInfo.imeiMd5, DeviceInfo.macMd5, DeviceInfo.androidIdMd5, DeviceInfo.bssidMd5, DeviceInfo.ssid);
        byte[] bytes = reqLogin.getData(context.getClientPubKey(), context.getShareTeaKey(), context.getRandomKey());

        log.info("{}:shareTeaKey:{}", qqUin, Util.convertByteToHexString(context.getShareTeaKey()));
        log.info("{}:tgtgtKey:{}", qqUin, Util.convertByteToHexString(context.getTgtgtKey()));

        ReqHead reqHead = new ReqHead("wtlogin.login", context);
        AqToServiceMsg toMsg = AqToServiceMsg.create0802(reqHead, bytes, context);

        AqFromServiceMsg fromServiceMsg = client.send(toMsg, TIME_OUT);
        if (Objects.isNull(fromServiceMsg)) {
            log.info("{}:登陆超时", qqUin);
            return LoginResult.TIMEOUT;
        }
        log.info("from service msg:{}", Util.convertByteToHexString(fromServiceMsg.getBodyData()));

        LoginResult loginResult = decodeLoginResp(fromServiceMsg, imgHandler, reqHead);
        if (Objects.equals(LoginResult.SUCCESS, loginResult)) {//如果登陆成功,发送心跳和ssoLoginMerge
            startHeartbeatThread();
            sendSSoLoginMerge();
        }
        return loginResult;
    }

    /**
     * 解析登陆结果
     * @param fromServiceMsg
     * @return
     */
    private LoginResult decodeLoginResp(AqFromServiceMsg fromServiceMsg, VfCodeHandler vfCodeHandler, ReqHead reqHead) {
        log.info("from service msg:{}", Util.convertByteToHexString(fromServiceMsg.getBodyData()));
        ByteBuffer buf = ByteBuffer.wrap(fromServiceMsg.getBodyData());

        buf.position(17);

        int decryptType = buf.getShort();
        int loginRespType = buf.get();

        int len = buf.remaining() - 1;
        byte[] enData = BufUtils.getBytesFromBuf(buf, len);
        log.info(Util.convertByteToHexString(enData));

        byte[] shareTeaKeyDecrypt = null;
        if (0x0000 == decryptType) {//shareKey 一层解密
            shareTeaKeyDecrypt = new Crypter().decrypt(enData, context.getShareTeaKey());
        } else if (0x0004 == decryptType) {//2层shareKey 解密
            shareTeaKeyDecrypt = new Crypter().decrypt(enData, context.getShareTeaKey());
            buf = ByteBuffer.wrap(shareTeaKeyDecrypt);
            int serverPubKeyLen = buf.getShort();
            byte[] serverPubKey = new byte[serverPubKeyLen];
            buf.get(serverPubKey);

            byte[] shareKey2 = ShareKeyFactory.calcShareKey(serverPubKey, context.getShareKey().getPrivateKey());
            log.info("shareKey2:{}", Util.convertByteToHexString(shareKey2));


            byte[] innerEnData = new byte[buf.remaining()];
            buf.get(innerEnData);
            shareTeaKeyDecrypt = new Crypter().decrypt(innerEnData, shareKey2);
        }

        log.info("decrypt by shareTeaKey:{}", Util.convertByteToHexString(shareTeaKeyDecrypt));
        buf = ByteBuffer.wrap(shareTeaKeyDecrypt);

        buf.getShort();
        final int status = buf.get();

        Map<Integer, TlvData> rdsMap = new HashMap<>(0);
        int objCount = buf.getShort();
        for (int i = 0; i < objCount; i++) {
            int objId = buf.getShort();
            int objDataLen = buf.getShort();
            byte[] objData = BufUtils.getBytesFromBuf(buf, objDataLen);
            //log.info("id:{}, len:{}, objData:{}", objId, objDataLen, Util.convertByteToHexString(objData));

            TlvData rds = new TlvData(objId, objDataLen, objData);
            rdsMap.put(objId, rds);
        }

        reqHead.setCookie(context.getCookie());
        long qqUin = context.getQqUin();
        switch (status) {
            case (byte)0x00:
                return sendStatSvcRegister(rdsMap);
            case (byte)0x01:
                log.info("{}:密码错误", qqUin);
                return LoginResult.PWERROR;
            case (byte)0x02:
                return processVfImg(rdsMap, reqHead, vfCodeHandler);
            case (byte)0x09:
                log.info("{}:服务器繁忙", qqUin);
                return LoginResult.SERVER_BUSY;
            case (byte)0x20:
                log.info("{}:被冻结", qqUin);
                return LoginResult.BLOCK;
            case (byte)0xA0:
                log.info("{}:需要手机验证码验证", qqUin);
                return processSmsVerify(rdsMap, reqHead, vfCodeHandler);
            case (byte)0xA3:
                log.info("{}:手机验证码错误", qqUin);
                return LoginResult.HAS_TEL_VFCODE;
            case (byte)0xED:
                log.info("{}:禁止登陆", qqUin);
                return LoginResult.FORBID_LOGIN;
            case (byte)0x9A:
                log.info("{}:网络异常", qqUin);
                return LoginResult.FORBID_LOGIN;
            default:
                log.info("{}:未知错误", qqUin);
                return LoginResult.OTHER_ERROR;
        }
    }

    /**
     * 处理手机验证码认证
     * @param rdsMap
     * @param reqHead
     * @param vfCodeHandler
     * @return
     */
    private LoginResult processSmsVerify(Map<Integer, TlvData> rdsMap, ReqHead reqHead, VfCodeHandler vfCodeHandler) {
        if (Objects.isNull(vfCodeHandler)) {
            log.info("{}:需要手机验证");
            return LoginResult.HAS_TEL_VFCODE;
        }

        TlvData rds0174 = rdsMap.get(0x0174);
        TlvData rds0104 = rdsMap.get(0x0104);

        // 提示需要手机验证码，然后去获取验证码
        if (rds0174 != null) {
            TlvData rds0178 = rdsMap.get(0x0178);
            byte[] telData = rds0178.getData();
            byte[] telBytes = Arrays.copyOfRange(telData, 6, 17);
            String tel = new String(telBytes);
            log.info("手机：{}", tel);
            context.setTel(tel);

            byte[] vfCodeToken = rds0104.getData();
            byte[] smsToken = rds0174.getData();
            context.setSmsToken(smsToken);
            ReqGetSmsCode reqGetSmsCode = new ReqGetSmsCode(context.getQqUin(), vfCodeToken, smsToken);
            byte[] reqGetSmsCodeData = reqGetSmsCode.getData(context.getClientPubKey(), context.getShareTeaKey(), context.getRandomKey());
            AqToServiceMsg toMsg = AqToServiceMsg.create0802(reqHead, reqGetSmsCodeData, context);
            AqFromServiceMsg fromServiceMsg = client.send(toMsg, TIME_OUT);
            if (Objects.isNull(fromServiceMsg)) {
                log.info("{}：获取手机验证码", context.getQqUin());
                return LoginResult.TIMEOUT;
            }
            return decodeLoginResp(fromServiceMsg, vfCodeHandler, reqHead);
        } else if (rds0104 != null) {
            // 手机验证码发送成功
            String vfCode = vfCodeHandler.process("短信验证：" + context.getTel());
            ReqCommitSmsCode reqCommitSmsCode = new ReqCommitSmsCode(context.getQqUin(), vfCode, context.getSmsToken(), rds0104.getData());
            byte[] reqCommitSmsCodeData = reqCommitSmsCode.getData(context.getClientPubKey(), context.getShareTeaKey(), context.getRandomKey());
            AqToServiceMsg toMsg = AqToServiceMsg.create0802(reqHead, reqCommitSmsCodeData, context);

            AqFromServiceMsg fromServiceMsg = client.send(toMsg, TIME_OUT);
            if (Objects.isNull(fromServiceMsg)) {
                log.info("{}：提交手机验证码超时", context.getQqUin());
                return LoginResult.TIMEOUT;
            }
            return decodeLoginResp(fromServiceMsg, vfCodeHandler, reqHead);

        }

        return LoginResult.HAS_TEL_VFCODE;
    }

    /**
     * 处理登陆验证码
     * @param rdsMap
     * @param reqHead
     * @param vfCodeHandler
     * @return
     */
    private LoginResult processVfImg(Map<Integer, TlvData> rdsMap, ReqHead reqHead, VfCodeHandler vfCodeHandler) {
        TlvData rds0104 = rdsMap.get(0x0104);
        TlvData rds0105 = rdsMap.get(0x0105);
        byte[] data = rds0105.getData();
        ByteBuffer buf = ByteBuffer.wrap(data);
        byte[] imgIdBytes = BufUtils.getBytesFromBuf(buf, buf.getShort());
        byte[] imgData = BufUtils.getBytesFromBuf(buf, buf.getShort());
        byte[] recv0104 = rds0104.getData();

        if (Objects.isNull(vfCodeHandler)) {
            log.info("{}:需要验证码");
            return LoginResult.VFCODE;
        }

        String vfCode = vfCodeHandler.process(new VfImg(imgData));
        ReqCommitVFCode reqCommitVFCode = new ReqCommitVFCode(context.getQqUin(), vfCode, imgIdBytes, recv0104);
        byte[] commitVFCodeData = reqCommitVFCode.getData(context.getClientPubKey(), context.getShareTeaKey(), context.getRandomKey());
        AqToServiceMsg toMsg = AqToServiceMsg.create0802(reqHead, commitVFCodeData, context);

        AqFromServiceMsg fromServiceMsg = client.send(toMsg, TIME_OUT);
        if (Objects.isNull(fromServiceMsg)) {
            log.info("{}：提交验证码超时", context.getQqUin());
            return LoginResult.TIMEOUT;
        }
        return decodeLoginResp(fromServiceMsg, vfCodeHandler, reqHead);
    }

    private LoginResult sendStatSvcRegister(Map<Integer, TlvData> rdsMap) {
        TlvData rds = rdsMap.get(0x0119);

        byte[] decrypt = new Crypter().decrypt(rds.getData(), context.getTgtgtKey());
        ByteBuffer buf = ByteBuffer.wrap(decrypt);
        int objCount = buf.getShort();

        for (int i = 0; i < objCount; i++) {
            int objId = buf.getShort();
            int objDataLen = buf.getShort();
            byte[] objData = BufUtils.getBytesFromBuf(buf, objDataLen);
            //log.info("id:{}, len:{}, objData:{}", objId, objDataLen, Util.convertByteToHexString(objData));

            rdsMap.put(objId, new TlvData(objId, objDataLen, objData));
        }

        TlvData stKeyRds = rdsMap.get(0x010E);
        if (Objects.nonNull(stKeyRds)) {
            context.setStKey(stKeyRds.getData());
        }
        TlvData rdsToken0143 = rdsMap.get(0x0143);
        if (Objects.nonNull(rdsToken0143)) {
            context.setToken0143(rdsToken0143.getData());
        }
        TlvData rds010a = rdsMap.get(0x010a);
        if (Objects.nonNull(rds010a)) {
            context.setToken010a(rds010a.getData());
        }
        TlvData rds0305 = rdsMap.get(0x0305);
        if (Objects.nonNull(rds0305)) {
            context.setSessionKey(rds0305.getData());
        }

        TlvData rds011a = rdsMap.get(0x011a);
        buf = ByteBuffer.wrap(rds011a.getData());
        buf.get();
        buf.get();
        int age = buf.get();
        int sex = buf.get();
        String nick = BufUtils.getStringFromBuf(buf, buf.get());
        context.setAge(age);
        context.setNick(nick);
        context.setSex(sex);
        log.info("nick:{},age:{},sex:{}", nick, age, sex);
        log.info("sessionKey:{}", Util.convertByteToHexString(context.getSessionKey()));

        context.incRequestId();
        ReqHead reqHead = new ReqHead("StatSvc.register", context);
        SvcReqRegister svcReqRegister = new SvcReqRegister(context.getQqUin(), DeviceInfo.imeiMd5, Const.PHONE_BRANDS, Const.PHONE_TYPE, Const.OS_VERSION);
        UniPacket uniPacket = new UniPacket(true);
        uniPacket.setFuncName("SvcReqRegister");
        uniPacket.setServantName("PushService");
        uniPacket.put("SvcReqRegister", svcReqRegister);

        byte[] encode = uniPacket.encode();

        AqToServiceMsg toMsg = AqToServiceMsg.create0801(reqHead, encode, context);
        AqFromServiceMsg fromServiceMsg = client.send(toMsg, TIME_OUT);
        if (Objects.isNull(fromServiceMsg)) {
            log.info("{}: 登陆SvcReqRegister返回超时", context.getQqUin());
            return LoginResult.TIMEOUT;
        }

        UniPacket up = new UniPacket(true);
        up.setEncodeName("utf-8");
        up.decode(fromServiceMsg.getBodyData());

        SvcRespRegister svcRespRegister = up.getByClass("SvcRespRegister", new SvcRespRegister());
        log.info("登陆成功,ip:{},port:{}", svcRespRegister.strClientIP, svcRespRegister.iClientPort);
        context.setStatus(Status.ONLINE);

        return LoginResult.SUCCESS;
    }

    /**
     * 发送 ssologinMerge包
     */
    private void sendSSoLoginMerge() {
        GetSimpleOnlineFriendInfoReq gofi = new GetSimpleOnlineFriendInfoReq();
        gofi.uin = context.getQqUin();
        gofi.ifShowTermType = 1;
        gofi.version = 5;

        UniPacket up = new UniPacket();
        up.useVersion3();
        up.setFuncName("GetSimpleOnlineFriendInfoReq");
        up.setServantName("mqq.IMService.FriendListServiceServantObj");
        up.put("FSOLREQ", gofi);
        byte[] buf = up.encode();

        SSOLoginMerge.BusiBuffItem item = new SSOLoginMerge.BusiBuffItem();
        item.NeedResp.set(true);
        item.ServiceCmd.set("friendlist.GetSimpleOnlineFriendInfoReq");
        item.BusiBuffLen.set(buf.length);
        item.BusiBuff.set(ByteStringMicro.copyFrom(buf, 4, buf.length - 4));
        item.SeqNo.set(context.incRequestId());

        SSOLoginMerge.BusiBuffData bbd = new SSOLoginMerge.BusiBuffData();
        bbd.BusiBuffVec.add(item);


        GetTroopListReqV2 troopListReqV2 = new GetTroopListReqV2();
        troopListReqV2.uin = context.getQqUin();
        troopListReqV2.shVersion = 5;
        troopListReqV2.bGroupFlagExt = 1;

        up = new UniPacket();
        up.useVersion3();
        up.setFuncName("GetTroopListReqV2");
        up.setServantName("mqq.IMService.FriendListServiceServantObj");
        up.put("GetTroopListReqV2", troopListReqV2);
        buf = up.encode();

        item = new SSOLoginMerge.BusiBuffItem();
        item.NeedResp.set(true);
        item.ServiceCmd.set("friendlist.GetTroopListReqV2");
        item.BusiBuffLen.set(buf.length);
        item.BusiBuff.set(ByteStringMicro.copyFrom(buf, 4, buf.length - 4));
        item.SeqNo.set(context.incRequestId());
        bbd.BusiBuffVec.add(item);

        byte[] zipData = ZipUtils.deflate(bbd.toByteArray());
        byte[] body = new byte[zipData.length + 4];
        Util.int32_to_buf(body, 0, body.length);
        System.arraycopy(zipData, 0, body, 4, zipData.length);

        context.incRequestId();
        ReqHead reqHead = new ReqHead("SSO.LoginMerge", context.getCookie());

        AqToServiceMsg toServiceMsg = AqToServiceMsg.create0901(reqHead, body, context);
        client.send(toServiceMsg);
        //该数据包的返回数据中没有requestId相对应
//        if (Objects.isNull(resp)) {
//            log.info("{}SSO.LoginMerge超时", context.getQqUin());
//        } else {
//            log.info("{}ssologinMerger:{}", resp.getQqUin(), Util.convertByteToHexString(resp.getBodyData()));
//        }
    }

    /**
     * 心跳线程
     */
    class HeartbeatThread extends Thread{

        private volatile boolean runFlag;

        protected HeartbeatThread() {
            runFlag = true;
            this.setDaemon(true);//心跳线程为后台线程
        }

        /**
         * 退出心跳线程
         */
        protected void exit() {
            runFlag = false;
        }

        @Override
        public void run() {
            //首次发送心跳延迟10秒
            ThreadUtils.sleep(10000);

            while(runFlag) {
                if (!isOnline()) {
                    System.out.println("掉线");
                    break;
                }
                try {
                    reTrySendHeartbeat();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //1分钟发一次心跳
                ThreadUtils.sleep(1 * 60 * 1000);
            }
        }
    }


    /**
     * 退出心跳线程
     */
    private void exitHeartbeatThread() {
        if (Objects.nonNull(heartbeatThread)) {
            heartbeatThread.exit();
            heartbeatThread = null;
        }
    }

    /**
     * 启动心跳线程
     */
    private void startHeartbeatThread() {
        exitHeartbeatThread();
        heartbeatThread = new HeartbeatThread();
        heartbeatThread.start();
    }

    public void testGetTroop(byte[] vecCookies) {
        //TODO

        GetTroopListReqV2 troopListReqV2 = new GetTroopListReqV2();
        troopListReqV2.uin = context.getQqUin();
        troopListReqV2.shVersion = 5;
        troopListReqV2.bGroupFlagExt = 1;
        troopListReqV2.vecCookies = vecCookies;

        UniPacket up = new UniPacket();
        up.useVersion3();
        up.setFuncName("GetTroopListReqV2");
        up.setServantName("mqq.IMService.FriendListServiceServantObj");
        up.put("GetTroopListReqV2", troopListReqV2);
        byte[] buf = up.encode();

        ReqHead reqHead = new ReqHead("friendlist.GetTroopListReqV2", new byte[0]);
        AqToServiceMsg toServiceMsg = AqToServiceMsg.create0901(reqHead, buf, context);

        AqFromServiceMsg send = this.client.send(toServiceMsg, 5000);
        if (Objects.isNull(send)) {
            log.info("获取群列表超时");
        } else {
            log.info("获取群列表成功:");
            //System.out.println(Util.convertByteToHexString(send.getBodyData()));
            client.handleGetTroopListReq(send);
        }
    }

    /**
     * 判断是否在线，3分钟没收到消息认为掉线
     * @return
     */
    public boolean isOnline() {
        return Objects.equals(context.getStatus(), Status.ONLINE) &&
                (System.currentTimeMillis() - context.getLastRecvMsgTime()) < 180000;
    }


    private void reTrySendHeartbeat() {
        GetSimpleOnlineFriendInfoReq gofi = new GetSimpleOnlineFriendInfoReq();
        gofi.uin = context.getQqUin();
        gofi.ifShowTermType = 1;
        gofi.version = 5;

        UniPacket up = new UniPacket();
        up.useVersion3();
        up.setFuncName("GetSimpleOnlineFriendInfoReq");
        up.setServantName("mqq.IMService.FriendListServiceServantObj");
        up.setRequestId(this.context.incRequestId());
        up.put("FSOLREQ", gofi);
        byte[] buf = up.encode();


        ReqHead reqHead = new ReqHead("friendlist.GetSimpleOnlineFriendInfoReq", this.context.getCookie());
        AqToServiceMsg toServiceMsg = AqToServiceMsg.create0901(reqHead, buf, context);

        FromServiceMsg send = reTrySendHeartbeat(toServiceMsg);
        if (Objects.isNull(send)) {
            log.info("心跳发送超时");
        } else {
            log.info("心跳发送成功");
        }
    }

    private FromServiceMsg reTrySendHeartbeat(AqToServiceMsg toServiceMsg) {
        int tryTimes = 2;
        for (int i = 0; i < tryTimes; i++) {
            FromServiceMsg send = this.client.send(toServiceMsg, TIME_OUT);
            if (Objects.nonNull(send)) {
                return send;
            }
        }
        return null;
    }

    /**
     * 请求群离线消息
     */
    public void getGroupOfflineMsg() {
        final int requestId = this.context.incRequestId();
        final long qqUin = this.context.getQqUin();

        UniPacket uniPacket = new UniPacket(true);
        SvcRequestRegisterNew srrn = new SvcRequestRegisterNew();
        srrn.ulRequestOptional = 0x01C2;
        uniPacket.setServantName("RegPrxySvc");
        uniPacket.setRequestId(requestId);

        SvcRequestGetMsgV2 c2cmsg = new SvcRequestGetMsgV2();
        c2cmsg.lUin = qqUin;
        c2cmsg.uDateTime = 1;
        c2cmsg.cRecivePic = 1;
        c2cmsg.shAbility = 0x0F;
        c2cmsg.cChannel = 0x04;
        c2cmsg.cInst = 1;
        c2cmsg.cChannelEx = 1;
        c2cmsg.shLatestRambleNumber = 0x14;
        c2cmsg.shOtherRambleNumber = 3;
        c2cmsg.cOnlineSyncFlag = 1;
        c2cmsg.lGeneralAbi = 1;

        srrn.c2cmsg = c2cmsg;

        SvcRequestPullGroupMsgSeq groupMsgSeq = new SvcRequestPullGroupMsgSeq();
        groupMsgSeq.vGroupInfo = new ArrayList();
        groupMsgSeq.filter = MsgFilter._LIMIT_10_AND_IN_3_DAYS;

        this.context.getVecTroopList().forEach(stTroopNum ->
                groupMsgSeq.vGroupInfo.add(new PullGroupSeqParam(stTroopNum.GroupCode, 0))
        );
        srrn.groupmsg = groupMsgSeq;
        srrn.cDisgroupMsgFilter = 0x01;
        srrn.cGroupMask = 0x02;
        srrn.uEndSeq = 121;

        uniPacket.put("req_OffMsg", srrn);
        uniPacket.put("req_PbOffMsg", Util.convertHexStringToByte("00 00 00 00 08 00 18 00 20 14 28 03 30 00 38 01 "));

        ReqHead reqHead = new ReqHead("RegPrxySvc.infoLogin", this.context.getCookie());
        AqToServiceMsg toServiceMsg = AqToServiceMsg.create0901(reqHead, uniPacket.encode(), context);

        AqFromServiceMsg fromServiceMsg = this.client.send(toServiceMsg, TIME_OUT);
        if (Objects.isNull(fromServiceMsg)) {
            log.info("RegPrxySvc.infoLogin发送超时");
        } else {
            log.info(fromServiceMsg.getRespHead().getServiceCmd());
        }
    }

    public String getNick() {
        return this.context.getNick();
    }

    /**
     * 下线
     */
    public void logout() {
        context.setStatus(Status.OFFLINE);
        exitHeartbeatThread();
        if (Objects.nonNull(this.client)) {
            try {
                this.client.close();
            } catch (Exception e) {
                //ignore exception
            }
        }
        this.client = null;
    }
}
